package org.ar.xmling;

import java.util.List;

import org.ar.xmling.dom.XMLTag;
import org.ar.xmling.dom.XMLTagAttribute;

public final class XMLUtil {

  private XMLUtil() {
    throw new UnsupportedOperationException("Can't create instance of util class");
  }

  public static XMLDocument stringToXMLDocument(String xmlString) {
    XMLDocument doc = new XMLDocument();
    List<XMLTag> tags = doc.createTagsList();
    StringBuilder sb = new StringBuilder(xmlString.trim());
    tags.addAll(parse(sb, null).getTags());
    return doc;
  }



  private static XMLParsePart parse(StringBuilder sb, String enterTagName) {
    // TODO don't parse tag value before new tag open scope. like <tag> label <em> hello <em> good
    // <tag>
    XMLParsePart result = new XMLParsePart();
    int tagBeginOpen = sb.indexOf("<");
    int tagEndStart = sb.indexOf("</");

    if (tagBeginOpen >= 0 && (tagBeginOpen < tagEndStart || tagEndStart == -1)) {
      XMLTag tag;
      // check end on tag type 1
      int tagBeginClose = sb.indexOf(">");
      int tagEndClose = sb.indexOf("/>");
      if (tagBeginClose >= 0 && (tagBeginClose < tagEndClose || tagEndClose == -1)) {
        // normalize
        int endOfNameIndex = sb.indexOf(" ");
        if (endOfNameIndex == -1 || tagBeginOpen >= endOfNameIndex) {
          endOfNameIndex = tagBeginClose;
        }
        if (endOfNameIndex >= 0) {
          // normalize
          if (endOfNameIndex > tagBeginClose) {
            endOfNameIndex = tagBeginClose;
          }

          String tagName = sb.substring(tagBeginOpen + 1, endOfNameIndex);
          String attrLine = sb.substring(endOfNameIndex, tagBeginClose);
          tag = new XMLTag(tagName);
          parseAttribute(tag, attrLine);

          StringBuilder nextSb = new StringBuilder(sb.substring(tagBeginClose + 1).trim());
          XMLParsePart part = parse(nextSb, tagName);
          tag.createTagsList().addAll(part.getTags());
          // check if has end of tag
          if (part.isCompleted()) {
            String bufferLine =
                part.getLastPartOfData().substring(part.getLastPartOfData().indexOf(">") + 1);
            // check if closed current tag
            if (part.getEndTagName().equals(tagName)) {
              tag.setValue(part.getTagValue());
              result.getTags().add(tag);
              XMLParsePart part2 = parse(new StringBuilder(bufferLine.trim()), enterTagName);
              result.getTags().addAll(part2.getTags());
              result.setCompleted(part2.isCompleted());
              result.setEndTagName(part2.getEndTagName());
              result.setLastPartOfData(part2.getLastPartOfData());
              result.setTagValue(part2.getTagValue());
            }
          }
        }
      } else {
        // check end of tag type 2
        if (tagEndClose >= 0) {
          int endOfNameIndex = sb.indexOf(" ");
          // normalize
          if (endOfNameIndex > tagEndClose) {
            endOfNameIndex = tagEndClose;
          }
          if (endOfNameIndex >= 0) {
            // tag has parameters on space after name
            String tagName = sb.substring(tagBeginOpen + 1, endOfNameIndex);
            String attrLine = sb.substring(endOfNameIndex, tagEndClose);
            tag = new XMLTag(tagName);
            parseAttribute(tag, attrLine);
          } else {
            // tag has not parametes and end without any space
            String tagName = sb.substring(tagBeginOpen + 1, tagEndClose);
            tag = new XMLTag(tagName);
          }
          // add current tag
          result.getTags().add(tag);
          // prepare next data
          StringBuilder nextSb = new StringBuilder(sb.substring(tagEndClose + 2).trim());
          XMLParsePart part = parse(nextSb, null);
          result.getTags().addAll(part.getTags());
          result.setCompleted(part.isCompleted());
          result.setEndTagName(part.getEndTagName());
          result.setLastPartOfData(part.getLastPartOfData());
        }
      }
    } else {
      if (tagEndStart >= 0) {
        // founded end of tag
        result.setEndTagName(sb.substring(tagEndStart + 2, sb.indexOf(">")));
        result.setCompleted(true);
        result.setLastPartOfData(sb.toString());
        // find value
        if (tagBeginOpen > 0) {
          result.setTagValue(sb.substring(0, tagBeginOpen));
        }
      } else {
        result.setCompleted(false);
      }
    }
    return result;
  }

  private static void parseAttribute(XMLTag tag, String attrLine) {
    // TODO Bad parse attribute with spaces
    String[] splittedAttr = attrLine.split(" ");
    List<XMLTagAttribute> attributes = tag.createAttributesList();
    if (!attrLine.isEmpty() && splittedAttr.length > 0) {
      for (int i = 0; i < splittedAttr.length; i++) {
        String attr = splittedAttr[i];
        String[] attrPair = attr.split("=");
        XMLTagAttribute attribute = new XMLTagAttribute();
        if (attrPair.length == 2) {
          attribute.setName(attrPair[0]);
          attribute.setValue(attrPair[1]);
        } else if (attrPair.length == 1) {
          attribute.setName(attrPair[0]);
        }
        attributes.add(attribute);
      }
    }
  }
}
